package com.shankar.narayan.ms.screen;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * Thread safe circular queue implementation in java. This implementation uses 2
 * markers to track the front and the rear of the queue to enable the FIFO model
 * 
 * The initialize, enqueue and dequeue methods are all synchronized to ensure
 * only one thread can access the queue at any time for these operations.
 */
public class CircularQueue {

	private int _queueLength;

	private int[] _circularQueue = {};

	private int _front;

	private int _rear;

	private int _count;

	public CircularQueue() {
	}

	public synchronized void initialize(int length) {
		_queueLength = length;
		_circularQueue = new int[_queueLength];
		_front = 0;
		_rear = -1;
		_count = 0;
	}

	// insert from rear
	public synchronized int enqueue(int item) {
		if ((_rear + 1) % _queueLength == _front && _count == _queueLength) {
			// full queue
			System.out.println("The queue is full.");
			return -1;
		}
		_rear = (_rear + 1) % _queueLength;
		_circularQueue[_rear] = item;
		_count++;

		return _rear;
	}

	// delete from front
	public synchronized int dequeue() {
		if (_front == _rear && _count == 0) {
			// empty queue
			System.out.println("The queue is empty. No elements to delete.");
			return -1;
		}
		_circularQueue[_front] = 0;
		_front = (_front + 1) % _queueLength;
		_count--;
		return _front;
	}

	public void displayQueue() {
		System.out.println("********** Circular Queue ***********\n");
		for (int i = 0; i < _queueLength; i++) {
			System.out.print(_circularQueue[i] + " ");
		}
		System.out.println("*************************************\n");
	}

	public static void main(String[] args) throws NumberFormatException,
			IOException {
		int length;
		char choice = '0';
		System.out.println("Enter the queue length");
		BufferedReader reader = new BufferedReader(new InputStreamReader(
				System.in));
		length = Integer.parseInt(reader.readLine());
		CircularQueue que = new CircularQueue();
		que.initialize(length);
		// menu
		while (choice != '4') {
			System.out.println("***************************************");
			System.out.println("1. Enqueue");
			System.out.println("2. Dequeue");
			System.out.println("3. Display Queue");
			System.out.println("4. Exit");
			BufferedReader readChoice = new BufferedReader(
					new InputStreamReader(System.in));
			choice = readChoice.readLine().charAt(0);
			switch (choice) {
			case '1':
				System.out.println("\nEnter the number to insert.");
				int num = Integer.parseInt(new BufferedReader(
						new InputStreamReader(System.in)).readLine());
				if (que.enqueue(num) < 0) {
					System.out.println("Error!");
				}
				break;
			case '2':
				System.out.println("\nDeleting element");
				int r = que.dequeue();
				if (r < 0) {
					System.out.println("Error!");
				} else {
					System.out.println("front now updated to : " + r);
				}
				break;
			case '3': {
				que.displayQueue();
				break;
			}
			case '4': {
				System.out.println("Exiting menu");
				System.exit(0);
				break;
			}
			}
		}
	}
}
